<?php
/**
 * SocialEngine
 *
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 * @version    $Id: Db.php 8303 2011-01-25 09:20:19Z john $
 * @author     John Boehr <j@webligo.com>
 */

/**
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 */
class Storage_Service_Db extends Storage_Service_Abstract
{
  // General

  protected $_type = 'db';

  protected $_adapter;

  protected $_table;

  protected $_chunkSize = 32768;

  public function __construct(array $config)
  {
    // Use specified database
    if( /* !empty($config['adapter']) && */
        !empty($config['host']) &&
        !empty($config['username']) &&
        !empty($config['password']) ) {
      $defaultAdapter = Engine_Api::_()->getDbtable('chunks', 'storage')->getAdapter();
      $adapterName = strtolower( !empty($config['adapter']) ? $config['adapter'] : array_pop(explode('_', get_class($defaultAdapter))) );
      $adapterNamespace = ( $adapterName == 'mysql' ? 'Engine_Db_Adapter' : 'Zend_Db_Adapter' );
      $this->_adapter = Zend_Db::factory($adapterName, array(
        'host' => $config['host'],
        'username' => $config['username'],
        'password' => $config['password'],
        'dbname' => $config['dbname'],
        'adapterNamespace' => $adapterNamespace,
      ));
      $this->_table = new Storage_Model_DbTable_Chunks(array(
        'adapter' => $this->_adapter,
        //'name' => 'engine4_storage_chunks',
      ));
    }
    
    // Use local database
    else {
      $this->_table = Engine_Api::_()->getDbtable('chunks', 'storage');
      $this->_adapter = $this->_table->getAdapter();
    }
    
    parent::__construct($config);
  }

  public function getType()
  {
    return $this->_type;
  }

  public function getTable()
  {
    if( null === $this->_table ) {
      $this->_table = Engine_Api::_()->getDbtable('chunks', 'storage');
    }

    return $this->_table;
  }

  public function getChunkSize()
  {
    return $this->_chunkSize;
  }


  // Accessors

  public function map(Storage_Model_File $model)
  {
    return Zend_Controller_Front::getInstance()->getRouter()
            ->assemble(array('module' => 'storage', 'controller' => 'index', 'action' => 'serve', 'file' => $model->getIdentity()), 'default', true);
  }

  public function store(Storage_Model_File $model, $file)
  {
    $table = $this->getTable();
    //$db = $table->getAdapter();
    //$db->beginTransaction();

    try
    {
      // Delete existing chunks
      $this->_remove($model);

      // Begin writing new chunks
      if( !($handle = fopen($file, 'rb')) ) {
        throw new Storage_Service_Exception('Unable to open file for storage');
      }

      $chunkLength = $this->getChunkSize();
      while( $data = fread($handle, $chunkLength) ) {
        $this->_write($model, $data);
      }
      
      //$db->commit();
    }

    catch( Exception $e )
    {
      //$db->rollBack();
      $this->_remove($model);
      throw $e;
    }

    return $model->getIdentity();
  }

  public function read(Storage_Model_File $model)
  {
    return $this->_read($model);
  }

  public function write(Storage_Model_File $model, $data)
  {
    $table = $this->getTable();
    //$db = $table->getAdapter();
    //$db->beginTransaction();

    try
    {
      // Delete existing chunks
      $this->_remove($model);

      // Begin writing new chunks
      $length = strlen($data);
      $chunkLength = $this->getChunkSize();
      $chunks = ceil($length / $chunkLength);

      for( $i = 0; $i < $chunks; $i++ )
      {
        $segment = substr($data, $i * $chunkLength, $chunkLength);
        $this->_write($model, $data);
      }
      
      //$db->commit();
    }

    catch( Exception $e )
    {
      //$db->rollBack();
      $this->_remove($model);
      throw $e;
    }

    return $model->getIdentity();
  }

  public function remove(Storage_Model_File $model)
  {
    $this->_remove($model);
  }

  public function temporary(Storage_Model_File $model)
  {
    $tmp_file = APPLICATION_PATH . '/public/temporary/storage/'.$model->getIdentity().'.'.$model->extension;
    $this->_mkdir(dirname($tmp_file));
    if( !$handle = fopen($tmp_file, 'wb') )
    {
      throw new Storage_Service_Exception('Unable to write to temporary file');
    }
    
    $i = 0;
    while( $string = $this->_read($model, $i++) )
    {
      fwrite($handle, $string);
    }
    
    return $tmp_file;
  }


  public function removeFile($path)
  {
    $this->_delete($path);
  }



  // Utilities
  
  protected function _remove($file)
  {
    if( $file instanceof Storage_Model_File )
    {
      $file = $file->getIdentity();
    }
    if( !is_numeric($file) )
    {
      throw new Storage_Service_Exception('Invalid argument passed to remove');
    }
    $this->getTable()->delete(array(
      'file_id = ?' => $file,
    ));
  }

  protected function _write($file, $data)
  {
    if( $file instanceof Storage_Model_File )
    {
      $file = $file->getIdentity();
    }
    if( !is_numeric($file) )
    {
      throw new Storage_Service_Exception('Invalid argument passed to remove');
    }
    if( strlen($data) > $this->getChunkSize() )
    {
      throw new Storage_Service_Exception('Data specified is greater than chunk length');
    }
    $this->getTable()->insert(array(
      'file_id' => $file,
      'data' => $data,
    ));
  }

  protected function _read($file, $index = null)
  {
    if( $file instanceof Storage_Model_File )
    {
      $file = $file->getIdentity();
    }
    if( !is_numeric($file) )
    {
      throw new Storage_Service_Exception('Invalid argument passed to remove');
    }

    $buf = '';
    $table = $this->getTable();
    $select = $table->select()
      ->where('file_id = ?', $file)
      ->order('chunk_id ASC');

    if( null !== $index ) {
      $select->limit(1, $index);
    }

    foreach( $table->fetchAll($select) as $chunk )
    {
      $buf .= $chunk->data;
    }

    return $buf;
  }
}